<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";
import {
  ElButton,
  ElUpload,
  ElMessage,
  type UploadRequestOptions,
  type UploadRawFile,
  type UploadFile,
} from "element-plus";

const fileList = ref([]);
const uploadStatus = ref<
  {
    uid: number;
    name: string;
    progress: number;
    status: "pending" | "uploading" | "finish" | "failed";
  }[]
>([]);
const maxConcurrentUploads = 3;
const uploadQueue: Array<UploadFile> = [];

let imageWorker: Worker | null = null;

const handleFileChange = (uploadFile: UploadFile) => {
  uploadQueue.push(uploadFile);
  uploadStatus.value.push({
    uid: uploadFile.uid,
    name: uploadFile.name,
    progress: 0,
    status: "pending",
  });
};

const beforeUpload = (rowFile: UploadRawFile): Promise<UploadRawFile> => {
  console.log("process before", `${rowFile.name}-${rowFile.size}`);
  return new Promise((resolve) => {
    if (!imageWorker) {
      resolve(rowFile);
      return;
    }

    imageWorker.postMessage({
      file: rowFile,
      quality: 0.7,
      targetFormat: "jpeg",
    });

    imageWorker.onmessage = (e) => {
      const processFile = e.data;
      console.log("process after", `${processFile.name}-${processFile.size}`);
      resolve(processFile);
    };
  });
};

// 控制并发上传，限制同时上传数量
const customHttpRequest = async (
  options: UploadRequestOptions & {
    onProgress: (event: { percent: number }) => void;
  }
) => {
  const { action, method, file, onError, onProgress, onSuccess } = options;

  const processFile = await beforeUpload(file);
  const storedProgress =
    localStorage.getItem(`upload-progress-${processFile.uid}`) || "0";
  let uploadedBytes = parseInt(storedProgress, 10);

  const updateProgesss = (event: ProgressEvent) => {
    const progress = ((uploadedBytes + event.loaded) / processFile.size) * 100;
    const fileStatus = uploadStatus.value.find(
      (item) => item.uid === processFile.uid
    );
    if (fileStatus) {
      fileStatus.progress = progress;
      fileStatus.status = "uploading";
    }
    onProgress({ percent: progress });
    localStorage.setItem(
      `upload-progress-${processFile.uid}`,
      `${uploadedBytes + event.loaded}`
    );
  };

  // 自定义分块上传实现断点续传
  const chunkSize = 1 * 1024 * 1024; // 1MB的分块大小
  const totalChunks = Math.ceil(processFile.size / chunkSize);
  let currentChunk = Math.floor(uploadedBytes / chunkSize);

  const uploadChunk = () => {
    if (currentChunk >= totalChunks) {
      localStorage.removeItem(`upload-progress-${processFile.uid}`);
      processQueue();
      onSuccess("上传成功");
      const fileStatus = uploadStatus.value.find(
        (item) => item.uid === processFile.uid
      );
      if (fileStatus) fileStatus.status = "finish";
      return;
    }

    const start = currentChunk * chunkSize;
    const end = Math.min(start + chunkSize, processFile.size);
    const chunk = processFile.slice(start, end);
    const formData = new FormData();
    formData.append("file", chunk);
    formData.append("filename", processFile.name);
    formData.append("currentChunk", `${currentChunk}`);
    formData.append("totalChunks", `${totalChunks}`);

    const xhr = new XMLHttpRequest();
    xhr.open(method, action, true);

    xhr.upload.onprogress = updateProgesss;

    xhr.onload = () => {
      uploadedBytes += chunk.size;
      currentChunk++;
      uploadChunk();
    };

    xhr.onerror = () => {
      const fileStatus = uploadStatus.value.find(
        (item) => item.uid === file.uid
      );
      if (fileStatus) fileStatus.status = "failed";
      onError(xhr.response);
    };

    xhr.send(formData);
  };

  uploadChunk();
};

const processQueue = () => {
  if (uploadQueue.length > 0) {
    const file = uploadQueue.shift()!;
    customHttpRequest({
      action: "http://localhost:3000/upload",
      method: "post",
      file: file.raw!,
      data: {},
      filename: file.name,
      headers: {},
      onProgress: ({ percent }) => {
        console.log("progress:", percent);
      },
      onSuccess: () => ElMessage.success(`文件 ${file.name} 上传成功`),
      onError: () => ElMessage.error(`文件 ${file.name} 上传失败`),
      withCredentials: false,
    });
  }
};

const startUpload = () => {
  for (let i = 0; i < Math.min(uploadQueue.length, maxConcurrentUploads); i++) {
    processQueue();
  }
};

onMounted(() => {
  imageWorker = new Worker(new URL("./worker/image.ts", import.meta.url));
});

onUnmounted(() => {
  if (imageWorker) {
    imageWorker.terminate();
    imageWorker = null;
  }
});
</script>

<template>
  <div>
    <el-upload
      :on-change="handleFileChange"
      :file-list="fileList"
      multiple
      :limit="10"
      :auto-upload="false"
    >
      <el-button type="primary">批量上传图片</el-button>
    </el-upload>

    <div>
      <el-button type="primary" @click="startUpload">开始上传</el-button>
    </div>
  </div>
</template>

<style lang="less" scoped></style>
